package com.wyl.auth.service.impl;

import cn.dev33.satoken.stp.StpUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.tree.Tree;
import cn.hutool.core.lang.tree.TreeUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.wyl.auth.dto.SysMenuDto;
import com.wyl.auth.dto.SysRoleMenuDto;
import com.wyl.auth.entity.SysMenu;
import com.wyl.auth.entity.SysRole;
import com.wyl.auth.entity.SysRoleMenu;
import com.wyl.auth.entity.SysUserRole;
import com.wyl.auth.exception.BusinessException;
import com.wyl.auth.mapper.SysMenuMapper;
import com.wyl.auth.req.SysMenuReq;
import com.wyl.auth.service.SysMenuService;
import com.wyl.auth.service.SysRoleMenuService;
import com.wyl.auth.service.SysUserRoleService;
import com.wyl.auth.service.SysUserService;
import com.wyl.auth.util.BeanAwareUtil;
import com.wyl.auth.vo.SysMenuVo;
import com.wyl.common.CommonPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.stream.Collectors;

/**
* @Description 菜单表
* @Author WuYiLong
* @Date 2024-12-02
*/
@Service
public class SysMenuServiceImpl extends ServiceImpl<SysMenuMapper, SysMenu> implements SysMenuService {

    @Autowired
    private SysRoleMenuService sysRoleMenuService;
    @Autowired
    private SysUserRoleService sysUserRoleService;

    @Override
    public Long save(SysMenuDto sysMenuDto) {
        if (sysMenuDto.getId() != null) {
            SysMenu sysMenu = getById(sysMenuDto.getId());
            BeanUtil.copyProperties(sysMenuDto, sysMenu);
            sysMenu.setUpdateTime(LocalDateTime.now());
            sysMenu.updateById();
            sysMenu.setUpdateUserId(StpUtil.getLoginIdAsLong());
            return sysMenu.getId();
        } else {
            SysMenu sysMenu = new SysMenu();
            BeanUtil.copyProperties(sysMenuDto, sysMenu);
            sysMenu.setCreateTime(LocalDateTime.now());
            sysMenu.setCreateUserId(StpUtil.getLoginIdAsLong());
            save(sysMenu);
            return sysMenu.getId();
        }
    }

    @Override
    public List<SysMenuVo> listSysMenu(SysMenuReq req) {
        LambdaQueryWrapper<SysMenu> sysMenuLambdaQueryWrapper = Wrappers.lambdaQuery(SysMenu.class);
        sysMenuLambdaQueryWrapper.like(StrUtil.isNotBlank(req.getLabel()), SysMenu::getLabel, req.getLabel());
        List<SysMenu> list = list(sysMenuLambdaQueryWrapper);
        List<SysMenuVo> sysMenuVos = list.stream().map(m -> {
            SysMenuVo sysMenuVo = new SysMenuVo();
            BeanUtil.copyProperties(m, sysMenuVo);
            return sysMenuVo;
        }).collect(Collectors.toList());
        return sysMenuVos;
    }

    @Override
    public List<Tree<Long>> treeSysMenu(SysMenuReq req) {
        List<SysMenuVo> sysMenuVos = listSysMenu(req);
        if(StrUtil.isNotBlank(req.getLabel())) {
            sysMenuVos = buildTreeMenu(sysMenuVos);
        }

        List<Tree<Long>> trees = TreeUtil.build(sysMenuVos, null, (t1, t2) -> {
            t2.setId(t1.getId());
            t2.setParentId(t1.getParentId());
            t2.setName(t1.getName());
            t2.putExtra("data", t1);
            t2.putExtra("label", t1.getLabel());
        });
        return trees;
    }

    private List<SysMenuVo> buildTreeMenu(List<SysMenuVo> list) {
        List<SysMenuVo> sysMenus = new ArrayList<>();
        for (SysMenuVo sysMenu : list) {
            buildMenu(sysMenu,sysMenus);
        }
        return sysMenus;
    }

    private void buildMenu(SysMenuVo sysMenu,List<SysMenuVo> sysMenus) {
        if(sysMenu.getParentId() == null){
            sysMenus.add(sysMenu);
        }
        sysMenus.add(sysMenu);
        SysMenu sysMenu1 = getById(sysMenu.getParentId());
        if(sysMenu1 == null){
            return;
        }
        SysMenuVo sysMenuVo = new SysMenuVo();
        BeanAwareUtil.copyProperties(sysMenu1,sysMenuVo);
        buildMenu(sysMenuVo,sysMenus);
    }

    @Override
    public List<Tree<Long>> treeSysMenu() {
        LambdaQueryWrapper<SysMenu> sysMenuLambdaQueryWrapper = Wrappers.lambdaQuery(SysMenu.class);
        sysMenuLambdaQueryWrapper.ne(SysMenu::getType, 3);
        List<Long> menuIds = getMenuIdsByUserId();
        if(CollUtil.isNotEmpty(menuIds)) {
            sysMenuLambdaQueryWrapper.in(SysMenu::getId, menuIds);
        }

        List<SysMenu> list = list(sysMenuLambdaQueryWrapper);
        List<Tree<Long>> trees = TreeUtil.build(list, null, (t1, t2) -> {
            t2.setId(t1.getId());
            t2.setParentId(t1.getParentId());
            t2.setName(t1.getName());
            if(StrUtil.isBlank(t1.getPath())){
                t1.setPath("");
            }
            t2.putExtra("data", t1);

        });
        return trees;
    }

    @Override
    public CommonPage<SysMenuVo> page(SysMenuReq req) {
        Page<SysMenu> page = new Page<>(req.getCurrentPage(), req.getPageSize());
        LambdaQueryWrapper<SysMenu> wrapper = Wrappers.lambdaQuery(SysMenu.class);
        wrapper.like(StrUtil.isNotBlank(req.getName()), SysMenu::getName, req.getName());
        Page<SysMenu> sysMenuPage = page(page, wrapper);
        List<SysMenuVo> sysMenuVos = sysMenuPage.getRecords().stream().map(m -> {
            SysMenuVo sysMenuVo = new SysMenuVo();
            BeanAwareUtil.copyProperties(m, sysMenuVo);
            return sysMenuVo;
        }).collect(Collectors.toList());

        CommonPage<SysMenuVo> commonPage = new CommonPage<>(req.getCurrentPage(), req.getPageSize());
        commonPage.setList(sysMenuVos);
        commonPage.setTotal((int) sysMenuPage.getTotal());
        return commonPage;
    }

    @Override
    public void deleteByIds(List<Long> ids) {
        LambdaQueryWrapper<SysRoleMenu> wrapper = Wrappers.lambdaQuery(SysRoleMenu.class);
        wrapper.in(SysRoleMenu::getMenuId, ids);
        long count = sysRoleMenuService.count(wrapper);
        if(count > 0) {
            throw new BusinessException("菜单已挂角色，不可删除");
        }

        baseMapper.deleteBatchIds(ids);
    }

    @Override
    public List<Tree<Long>> listCatalog() {
        LambdaQueryWrapper<SysMenu> sysMenuLambdaQueryWrapper = Wrappers.lambdaQuery(SysMenu.class);
        sysMenuLambdaQueryWrapper.in(SysMenu::getType,1,2);
        List<SysMenu> sysMenus = baseMapper.selectList(sysMenuLambdaQueryWrapper);
        List<Tree<Long>> trees = TreeUtil.build(sysMenus, null, (t1, t2) -> {
            t2.setId(t1.getId());
            t2.setParentId(t1.getParentId());
            t2.setName(t1.getName());
            t2.putExtra("label", t1.getLabel());
        });
        return trees;
    }

    @Override
    public List<Long> getMenuIdsByUserId() {
        String userId = StpUtil.getLoginIdAsString();
        if(StrUtil.isNotBlank(userId)) {
            LambdaQueryWrapper<SysUserRole> sysUserRoleWrapper = Wrappers.lambdaQuery(SysUserRole.class);
            sysUserRoleWrapper.eq(SysUserRole::getUserId,userId);
            List<SysUserRole> userRoles = sysUserRoleService.list(sysUserRoleWrapper);
            if(CollUtil.isEmpty(userRoles)) {
                return Collections.emptyList();
            }
            List<Long> roleIds = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
            LambdaQueryWrapper<SysRoleMenu> sysRoleMenuWrapper = Wrappers.lambdaQuery(SysRoleMenu.class);
            sysRoleMenuWrapper.in(SysRoleMenu::getRoleId,roleIds);
            List<SysRoleMenu> roleMenus = sysRoleMenuService.list(sysRoleMenuWrapper);
            if(CollUtil.isEmpty(roleMenus)) {
                return Collections.emptyList();
            }
            List<Long> menuIds = roleMenus.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
            return menuIds;
        }
        return Collections.emptyList();
    }

    @Override
    public List<String> getMenuCodesByUserId(Long userId) {
        if(userId != null) {
            LambdaQueryWrapper<SysUserRole> sysUserRoleWrapper = Wrappers.lambdaQuery(SysUserRole.class);
            sysUserRoleWrapper.eq(SysUserRole::getUserId,userId);
            List<SysUserRole> userRoles = sysUserRoleService.list(sysUserRoleWrapper);
            if(CollUtil.isEmpty(userRoles)) {
                return Collections.emptyList();
            }
            List<Long> roleIds = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
            LambdaQueryWrapper<SysRoleMenu> sysRoleMenuWrapper = Wrappers.lambdaQuery(SysRoleMenu.class);
            sysRoleMenuWrapper.in(SysRoleMenu::getRoleId,roleIds);
            List<SysRoleMenu> roleMenus = sysRoleMenuService.list(sysRoleMenuWrapper);
            if(CollUtil.isEmpty(roleMenus)) {
                return Collections.emptyList();
            }
            List<Long> menuIds = roleMenus.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
            List<SysMenu> sysMenus = listByIds(menuIds);
            List<String> collect = sysMenus.stream().map(SysMenu::getCode).collect(Collectors.toList());
            return collect;
        }
        return Collections.emptyList();
    }

    @Override
    public List<String> getUrlByUserId(Long userId) {
        if(userId != null) {
            LambdaQueryWrapper<SysUserRole> sysUserRoleWrapper = Wrappers.lambdaQuery(SysUserRole.class);
            sysUserRoleWrapper.eq(SysUserRole::getUserId,userId);
            List<SysUserRole> userRoles = sysUserRoleService.list(sysUserRoleWrapper);
            if(CollUtil.isEmpty(userRoles)) {
                return Collections.emptyList();
            }
            List<Long> roleIds = userRoles.stream().map(SysUserRole::getRoleId).collect(Collectors.toList());
            LambdaQueryWrapper<SysRoleMenu> sysRoleMenuWrapper = Wrappers.lambdaQuery(SysRoleMenu.class);
            sysRoleMenuWrapper.in(SysRoleMenu::getRoleId,roleIds);
            List<SysRoleMenu> roleMenus = sysRoleMenuService.list(sysRoleMenuWrapper);
            if(CollUtil.isEmpty(roleMenus)) {
                return Collections.emptyList();
            }
            List<Long> menuIds = roleMenus.stream().map(SysRoleMenu::getMenuId).collect(Collectors.toList());
            List<SysMenu> sysMenus = listByIds(menuIds);
            List<String> collect = sysMenus.stream().filter(f->StrUtil.isNotBlank(f.getUrl())).map(SysMenu::getUrl).collect(Collectors.toList());
            return collect;
        }
        return Collections.emptyList();
    }
}
